/******************************************************************************************************************
 * File: IsAliveMonitor.java
 * Course: 17655
 * Project: Assignment A3
 * Versions:
 *	1.0 March 2011 - RAA
 *          base
 *
 * Description:
 *    Monitor that watches for the liveness of the devices (sensors) in the system.
 *    As soon as it connects to the EventManager, sends a request for heartbeat that all the sensors will
 *    receive as an indication that they should be sending regular heartbeats.
 *
 *
 *
 * Internal Methods:
 *
 *
 ******************************************************************************************************************/
package Monitors;

import InstrumentationPackage.*;
import EventPackage.*;
import java.text.SimpleDateFormat;
import java.util.*;

class IsAliveMonitor extends Thread {

    /**
     * @return the time_to_live
     */
    public static int getTime_to_live() {
        return time_to_live;
    }

    /**
     * @param aTime_to_live the time_to_live to set
     */
    public static void setTime_to_live(int aTime_to_live) {
        time_to_live = aTime_to_live;
    }

    /**
     * @return the retry_interval
     */
    public static int getRetry_interval() {
        return retry_interval;
    }

    /**
     * @param aRetry_interval the retry_interval to set
     */
    public static void setRetry_interval(int aRetry_interval) {
        retry_interval = aRetry_interval;
    }
    private EventManagerInterface em = null;        // Interface object to the event manager
    private String EvtMgrIP = null;			// Event Manager IP address
    boolean Registered = true;			// Signifies that this class is registered with an event manager.
    MessageWindow mw = null;			// This is the message window
    private List<Long> registeredDevicesLastHeartbeatMillis = new ArrayList<Long>();
    private List<Indicator> registeredDevicesIndicators = new ArrayList<Indicator>();
    private List<String> registeredDevicesNames = new ArrayList<String>();
    private List<String> registeredDevicesDescs = new ArrayList<String>();
    //public static final Long yellowWarningHeartBeatDelayMillis = 30000L;
    //public static final Long redWarningHeartBeatDelayMillis = 60000L;
    private static int time_to_live;
    private static int retry_interval;
    SimpleDateFormat TimeStampFormat = new SimpleDateFormat("yyyy MM dd::hh:mm:ss:SSS");

    public IsAliveMonitor() {
        // event manager is on the local system

        try {
            // Here we create an event manager interface object. This assumes
            // that the event manager is on the local machine

            em = new EventManagerInterface(getRetry_interval(), getTime_to_live());

        } catch (Exception e) {
            System.out.println("ECSMonitor::Error instantiating event manager interface: " + e);
            Registered = false;

        } // catch

    } //Constructor

    public IsAliveMonitor(String EvmIpAddress) {
        // event manager is not on the local system

        EvtMgrIP = EvmIpAddress;

        try {
            // Here we create an event manager interface object. This assumes
            // that the event manager is NOT on the local machine

            em = new EventManagerInterface(EvtMgrIP, getRetry_interval(), getTime_to_live());
        } catch (Exception e) {
            System.out.println("IsAliveMonitor::Error instantiating event manager interface: " + e);
            Registered = false;

        } // catch

    } // Constructor

    /**
     *
     * As soon as it registers, this monitor sends a 'Request for Heartbeat Tracking' (70) message
     *
     * Whenever it receives a 'Heartbeat Registration' (-70) messages from a sensor,
     * either because they have just arrived at the system or in response to the monitor's request,
     * it adds it to the list of devices tracked for liveness
     *
     * It keeps a list of devices tracked for liveness
     *
     *
     *
     */
    public void run() {
        Event Evt = null;			// Event object
        EventQueue eq = null;			// Message Queue
        int EvtId = 0;				// User specified event ID
        float CurrentTemperature = 0;           // Current temperature as reported by the temperature sensor
        float CurrentHumidity = 0;		// Current relative humidity as reported by the humidity sensor
        int Delay = 1000;			// The loop delay (1 second)
        boolean Done = false;			// Loop termination flag
        boolean ON = true;			// Used to turn on heaters, chillers, humidifiers, and dehumidifiers
        boolean OFF = false;			// Used to turn off heaters, chillers, humidifiers, and dehumidifiers

        if (em != null) {

            mw = new MessageWindow("== Service maintenance console monitor starting ==", 0, 0);
            System.out.println("== Service maintenance console monitor starting ==");

            try {
                eq = em.GetEventQueue();
            } catch (Exception e) {
                mw.WriteMessage("Error getting event queue::" + e);
            }

            mw.WriteMessage("Registered with the event manager.");

            try {
                mw.WriteMessage("   Participant id: " + em.GetMyId());
                mw.WriteMessage("   Registration Time: " + em.GetRegistrationTime());

            } // try
            catch (Exception e) {
                System.out.println("Error:: " + e);

            } // catch


            // Request that devices register for heartbeat tracking
            RequestHeartbeatRegistration();

            /********************************************************************
             ** Here we start the main simulation loop
             *********************************************************************/
            while (!Done) {
                // Here we get our event queue from the event manager

                try {
                    eq = em.GetEventQueue();

                } // try
                catch (Exception e) {
                    mw.WriteMessage("Error getting event queue::" + e);

                } // catch

                // If there are messages in the queue, we read through them.
                // We are looking for EventIDs = -70 or -71. Event IDs of -70 are sensors
                // registering for heartbeat tracking; event IDs of -71 are devices heartbeats

                int qlen = eq.GetSize();

                for (int i = 0; i < qlen; i++) {
                    Evt = eq.GetEvent();

                    // A device is registering for heartbeat tracking
                    if (Evt.GetEventId() == -70) // Device registration
                    {
                        // each device is registered in the 3 lists:
                        // the list of device ids, the list of indicators,
                        // and the list of last time they were seen
                        String [] splt = Evt.GetMessage().split("\\|");
                        String name = splt[0];
                        String desc = "";
                        try {
                            desc = splt[1];
                        } catch (Exception e) {}

                        if (!registeredDevicesNames.contains(name)) {
                            registeredDevicesNames.add(name);
                            registeredDevicesDescs.add(desc);

                            Indicator newInd = new Indicator("Connected at " + TimeStampFormat.format(new Date()), mw.GetX() + mw.Width(), 0);
                            newInd.setTitle(name);

                            registeredDevicesIndicators.add(newInd);
                            registeredDevicesLastHeartbeatMillis.add(new Date().getTime());
                            // Request that devices register for heartbeat tracking
                        }

                        RequestHeartbeatRegistration();

                    } else if (Evt.GetEventId() == -71) // Heartbeat
                    {
                        // Update device's last heartbeat time
                        String [] splt = Evt.GetMessage().split("\\|");
                        String name = splt[0];
                        String desc = "";
                        try {
                            desc = splt[1];
                        } catch (Exception e) {}

                        if (registeredDevicesNames.contains(name)) {
                            int index = registeredDevicesNames.indexOf(name);
                            registeredDevicesLastHeartbeatMillis.remove(index);
                            registeredDevicesLastHeartbeatMillis.add(index, new Date().getTime());

                            mw.WriteMessage("Heartbeat received from device '" + registeredDevicesNames.get(index) + "' at " + new Date(registeredDevicesLastHeartbeatMillis.get(index)));
                        } else {
                            registeredDevicesNames.add(name);
                            registeredDevicesDescs.add(desc);

                            Indicator newInd = new Indicator("Connected at " + TimeStampFormat.format(new Date()), mw.GetX() + mw.Width(), 0);
                            newInd.setTitle(name);

                            registeredDevicesIndicators.add(newInd);
                            registeredDevicesLastHeartbeatMillis.add(new Date().getTime());
                            // Request that devices register for heartbeat tracking
                        }
                    }
                }

                Long now = new Date().getTime();
                for (int i = 0; i < registeredDevicesNames.size(); i++) {

                    String desc = registeredDevicesDescs.get(i);

                    if (now - registeredDevicesLastHeartbeatMillis.get(i) > ServiceMaintenanceConsole.warning_time) {
                        mw.WriteMessage("No heartbeat received from device '" + registeredDevicesNames.get(i) + "' since " + new Date(registeredDevicesLastHeartbeatMillis.get(i)));
                        if (now - registeredDevicesLastHeartbeatMillis.get(i) > ServiceMaintenanceConsole.error_time) {
                            registeredDevicesIndicators.get(i).SetLampColorAndMessage(desc+" - Last heartbeat received at " + new Date(registeredDevicesLastHeartbeatMillis.get(i)), 3);
                        } else {
                            registeredDevicesIndicators.get(i).SetLampColorAndMessage(desc+" - Last heartbeat received at " + new Date(registeredDevicesLastHeartbeatMillis.get(i)), 2);
                        }
                    } else {
                        registeredDevicesIndicators.get(i).SetLampColorAndMessage(desc+" - Last heartbeat received at " + new Date(registeredDevicesLastHeartbeatMillis.get(i)), 1);
                    }
                }

                try {
                    Thread.sleep(Delay);

                } // try
                catch (Exception e) {
                    System.out.println("Sleep error:: " + e);

                } // catch

            } // while

        } else {

            System.out.println("Unable to register with the event manager.\n\n");

        } // if

    } // main

    /***************************************************************************
     * CONCRETE METHOD:: IsRegistered
     * Purpose: This method returns the registered status
     *
     * Arguments: none
     *
     * Returns: boolean true if registered, false if not registered
     *
     * Exceptions: None
     *
     ***************************************************************************/
    public boolean IsRegistered() {
        return (Registered);

    } // SetTemperatureRange

    /***************************************************************************
     * CONCRETE METHOD:: Halt
     * Purpose: This method posts an event that stops the environmental control
     *		   system.
     *
     * Arguments: none
     *
     * Returns: none
     *
     * Exceptions: Posting to event manager exception
     *
     ***************************************************************************/
    public void Halt() {
        mw.WriteMessage("***HALT MESSAGE RECEIVED - SHUTTING DOWN SYSTEM***");

        // Here we create the stop event.

        Event evt;

        evt = new Event((int) 99, "XXX");

        // Here we send the event to the event manager.

        try {
            em.SendEvent(evt);
        } // try
        catch (Exception e) {
            System.out.println("Error sending halt message:: " + e);

        } // catch

    } // Halt

    /***************************************************************************
     * CONCRETE METHOD:: RequestHeartbeatRegistration
     * Purpose: This method posts an event that requests that sensors register for
     * heartbeat tracking and start sending heartbeat
     *
     * Arguments: none
     *
     * Returns: none
     *
     * Exceptions: Posting to event manager exception
     *
     ***************************************************************************/
    public void RequestHeartbeatRegistration() {
        mw.WriteMessage("IsAliveMonitoring active. Requesting that sensors register for heartbeats tracking.");

        // Here we create the request for heartbeat event.
        Event evt;

        evt = new Event((int) 70, "XXX");

        // Here we send the event to the event manager.

        try {
            em.SendEvent(evt);

        } catch (Exception e) {
            System.out.println("Error sending request for heartbeat registration message:: " + e);
        }

    }
} // IsAliveMonitor

